package com.gitee.wsl.common.draw.gesture

import android.graphics.PointF
import android.os.SystemClock
import android.view.MotionEvent
import android.view.animation.DecelerateInterpolator
import android.view.animation.Interpolator
import com.gitee.wsl.common.draw.gesture.computator.ChartCommutator
import com.gitee.wsl.common.draw.area.Viewport
import timber.log.Timber

/**
 * Encapsulates zooming functionality.
 */
class ChartZoomer(val commutator: ChartCommutator) {

    var zoomType: ZoomType by commutator::zoomType

    private val zoomer: ZoomerCompat = ZoomerCompat()
    private val zoomFocalPoint = PointF() // Used for double tap zoom
    private val viewportFocus = PointF()
    private val scrollerStartViewport = Viewport() // Used only for zooms and flings

    fun startZoom(e: MotionEvent): Boolean {
        Timber.d("Zoomer start.. ")
        zoomer.forceFinished(true)
        scrollerStartViewport.set(commutator.currentViewport)
        if (!commutator.rawPixelsToDataPoint(e.x, e.y, zoomFocalPoint)) {
            // Focus point is not within content area.
            return false
        }
        zoomer.startZoom(ZOOM_AMOUNT)
        return true
    }

    fun computeZoom(): Boolean {
        Timber.d("Zoomer compute..current zoom value:${zoomer.currZoom} ")
        if (zoomer.computeZoom()) {
            // Performs the zoom since a zoom is in progress.
            val newWidth = (1.0f - zoomer.currZoom) * scrollerStartViewport.width
            val newHeight = (1.0f - zoomer.currZoom) * scrollerStartViewport.height
            val pointWithinViewportX = ((zoomFocalPoint.x - scrollerStartViewport.left) / scrollerStartViewport.width)
            val pointWithinViewportY = ((zoomFocalPoint.y - scrollerStartViewport.bottom) / scrollerStartViewport.height)
            val left = zoomFocalPoint.x - newWidth * pointWithinViewportX
            val top = zoomFocalPoint.y + newHeight * (1 - pointWithinViewportY)
            val right = zoomFocalPoint.x + newWidth * (1 - pointWithinViewportX)
            val bottom = zoomFocalPoint.y - newHeight * pointWithinViewportY
            setCurrentViewport(left, top, right, bottom)
            return true
        }
        return false
    }

    fun scale( focusX: Float, focusY: Float, scale: Float): Boolean {
        Timber.d("Zoomer scale focusX:$focusX, focusY:$focusY, scale:$scale")
        /**
         * Smaller viewport means bigger zoom so for zoomIn scale should have value <1, for zoomOout >1
         */
        val newWidth = scale * commutator.currentViewport.width
        val newHeight = scale * commutator.currentViewport.height
        if (!commutator.rawPixelsToDataPoint(focusX, focusY, viewportFocus)) {
            // Focus point is not within content area.
            return false
        }
        val left = viewportFocus.x - (focusX - commutator.contentRectMinusAllMargins.left) * (newWidth / commutator.contentRectMinusAllMargins.width())
        val top = viewportFocus.y + (focusY - commutator.contentRectMinusAllMargins.top) * (newHeight / commutator.contentRectMinusAllMargins.height())
        val right = left + newWidth
        val bottom = top - newHeight
        setCurrentViewport( left, top, right, bottom)
        return true
    }

    private fun setCurrentViewport(
        left: Float,
        top: Float,
        right: Float,
        bottom: Float
    ) {
        val currentViewport = commutator.currentViewport

        when(zoomType){
            ZoomType.HORIZONTAL_AND_VERTICAL -> commutator.setCurrentViewport(left, top, right, bottom)
            ZoomType.HORIZONTAL -> commutator.setCurrentViewport(left, currentViewport.top, right, currentViewport.bottom)
            ZoomType.VERTICAL -> commutator.setCurrentViewport(currentViewport.left, top, currentViewport.right, bottom)
            else -> {}
        }
    }

    companion object {
        const val ZOOM_AMOUNT = 0.25f
    }
}

/**
 * A simple class that animates double-touch zoom gestures. Functionally similar to a [android.widget.Scroller].
 */
class ZoomerCompat(/**
                    * The interpolator, used for making zooms animate 'naturally.'
                    */
                   val mInterpolator: Interpolator = DecelerateInterpolator(),
                   /**
                    * The total animation duration for a zoom.
                    */
                   val mAnimationDurationMillis: Long = DEFAULT_SHORT_ANIMATION_DURATION
) {

    /**
     * Whether or not the current zoom has finished.
     */
    private var mFinished = true
    /**
     * Returns the current zoom level.
     *
     * @see android.widget.Scroller.getCurrX
     */
    /**
     * The current zoom value; computed by [.computeZoom].
     */
    var currZoom = 0f
        private set

    /**
     * The time the zoom started, computed using [SystemClock.elapsedRealtime].
     */
    private var mStartRTC: Long = 0

    /**
     * The destination zoom factor.
     */
    private var mEndZoom = 0f


    /**
     * Forces the zoom finished state to the given value. Unlike [.abortAnimation], the current zoom value isn't
     * set to the ending value.
     *
     * @see android.widget.Scroller.forceFinished
     */
    fun forceFinished(finished: Boolean) {
        mFinished = finished
    }

    /**
     * Aborts the animation, setting the current zoom value to the ending value.
     *
     * @see android.widget.Scroller.abortAnimation
     */
    fun abortAnimation() {
        mFinished = true
        currZoom = mEndZoom
    }

    /**
     * Starts a zoom from 1.0 to (1.0 + endZoom). That is, to zoom from 100% to 125%, endZoom should by 0.25f.
     *
     * @see android.widget.Scroller.startScroll
     */
    fun startZoom(endZoom: Float) {
        mStartRTC = SystemClock.elapsedRealtime()
        mEndZoom = endZoom
        mFinished = false
        currZoom = 1f
    }

    /**
     * Computes the current zoom level, returning true if the zoom is still active and false if the zoom has finished.
     *
     * @see android.widget.Scroller.computeScrollOffset
     */
    fun computeZoom(): Boolean {
        if (mFinished) {
            return false
        }
        val tRTC = SystemClock.elapsedRealtime() - mStartRTC
        if (tRTC >= mAnimationDurationMillis) {
            mFinished = true
            currZoom = mEndZoom
            return false
        }
        val t = tRTC * 1f / mAnimationDurationMillis
        currZoom = mEndZoom * mInterpolator.getInterpolation(t)
        return true
    }

    companion object {
        private const val DEFAULT_SHORT_ANIMATION_DURATION = 200L
    }
}

enum class ZoomType {
    HORIZONTAL, VERTICAL, HORIZONTAL_AND_VERTICAL,NONE
}